package panda.lang.escape;

import java.io.IOException;

/**
 * Translates codepoints to their XML numeric entity escaped value.
 *
 */
public class NumericEntityEscaper extends CodePointTranslator {

	private final int below;
	private final int above;
	private final boolean between;

	/**
	 * <p>
	 * Constructs a <code>NumericEntityEscaper</code> for the specified range. This is the
	 * underlying method for the other constructors/builders. The <code>below</code> and
	 * <code>above</code> boundaries are inclusive when <code>between</code> is <code>true</code>
	 * and exclusive when it is <code>false</code>.
	 * </p>
	 * 
	 * @param below int value representing the lowest codepoint boundary
	 * @param above int value representing the highest codepoint boundary
	 * @param between whether to escape between the boundaries or outside them
	 */
	private NumericEntityEscaper(final int below, final int above, final boolean between) {
		this.below = below;
		this.above = above;
		this.between = between;
	}

	/**
	 * <p>
	 * Constructs a <code>NumericEntityEscaper</code> for all characters.
	 * </p>
	 */
	public NumericEntityEscaper() {
		this(0, Integer.MAX_VALUE, true);
	}

	/**
	 * <p>
	 * Constructs a <code>NumericEntityEscaper</code> below the specified value (exclusive).
	 * </p>
	 * 
	 * @param codepoint below which to escape
	 * @return the newly created {@code NumericEntityEscaper} instance
	 */
	public static NumericEntityEscaper below(final int codepoint) {
		return outsideOf(codepoint, Integer.MAX_VALUE);
	}

	/**
	 * <p>
	 * Constructs a <code>NumericEntityEscaper</code> above the specified value (exclusive).
	 * </p>
	 * 
	 * @param codepoint above which to escape
	 * @return the newly created {@code NumericEntityEscaper} instance
	 */
	public static NumericEntityEscaper above(final int codepoint) {
		return outsideOf(0, codepoint);
	}

	/**
	 * <p>
	 * Constructs a <code>NumericEntityEscaper</code> between the specified values (inclusive).
	 * </p>
	 * 
	 * @param codepointLow above which to escape
	 * @param codepointHigh below which to escape
	 * @return the newly created {@code NumericEntityEscaper} instance
	 */
	public static NumericEntityEscaper between(final int codepointLow, final int codepointHigh) {
		return new NumericEntityEscaper(codepointLow, codepointHigh, true);
	}

	/**
	 * <p>
	 * Constructs a <code>NumericEntityEscaper</code> outside of the specified values (exclusive).
	 * </p>
	 * 
	 * @param codepointLow below which to escape
	 * @param codepointHigh above which to escape
	 * @return the newly created {@code NumericEntityEscaper} instance
	 */
	public static NumericEntityEscaper outsideOf(final int codepointLow, final int codepointHigh) {
		return new NumericEntityEscaper(codepointLow, codepointHigh, false);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean translate(final int codepoint, final Appendable out) throws IOException {
		if (between) {
			if (codepoint < below || codepoint > above) {
				return false;
			}
		}
		else {
			if (codepoint >= below && codepoint <= above) {
				return false;
			}
		}

		out.append("&#");
		out.append(Integer.toString(codepoint, 10));
		out.append(';');
		return true;
	}
}
